﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.IO;
using System.Diagnostics;

namespace GenericLibrary.Xml {

    //public class XmlObjectSerializer {

    //    public delegate bool XmlObjectParser<T>( T t, ref string name, IDictionary<string, object> attributes, IList<object> childs );

    //    private Dictionary<Type, XmlObjectParser<object>> _typedParsers;
    //    private Dictionary<string, object> _attributesBuffer;

    //    public void AddParser<T>( XmlObjectParser<T> typedParser ) {
    //        try {
    //            Type type = typeof( T );
    //            XmlObjectParser<object> parserAdapter = delegate( object obj, ref string name, IDictionary<string, object> attributes, IList<object> childs ) {
    //                return typedParser( ( T ) obj, ref name, attributes, childs );
    //            };
    //            if( this._typedParsers.ContainsKey( type ) ) {
    //                this._typedParsers[ type ] += parserAdapter;
    //            } else {
    //                this._typedParsers[ type ] = parserAdapter;
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public XmlObjectSerializer() {
    //        this._typedParsers = new Dictionary<Type, XmlObjectParser<object>>();
    //        this._attributesBuffer = new Dictionary<string, object>();
    //    }

    //    private void SerializeObject( object obj, XmlWriter writer ) {
    //        try {
    //            if( obj is IXPathNavigable ) {
    //                writer.WriteNode( ( obj as IXPathNavigable ).CreateNavigator(), true );
    //            } else if( obj is XmlReader ) {
    //                using( XmlReader reader = obj as XmlReader ) {
    //                    writer.WriteNode( reader, true );
    //                }
    //            } else {
    //                Type type = obj.GetType();
    //                if( this._typedParsers.ContainsKey( type ) ) {
    //                    string name;
    //                    List<object> childs = new List<object>();
    //                    foreach( XmlObjectParser<object> parser in this._typedParsers[ type ].GetInvocationList() ) {
    //                        name = null;
    //                        childs.Clear();
    //                        this._attributesBuffer.Clear();
    //                        if( parser( obj, ref name, this._attributesBuffer, childs ) ) {
    //                            writer.WriteStartElement( name );
    //                            foreach( string attributeName in this._attributesBuffer.Keys ) {
    //                                writer.WriteStartAttribute( attributeName );
    //                                writer.WriteValue( this._attributesBuffer[ attributeName ] );
    //                                writer.WriteEndAttribute();
    //                            }
    //                            foreach( object child in childs ) {
    //                                this.SerializeObject( child, writer );
    //                            }
    //                            writer.WriteEndElement();
    //                            break;
    //                        }
    //                    }
    //                }
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public void Serialize( object obj, XmlWriter output ) {
    //        try {
    //            output.WriteStartDocument();
    //            this.SerializeObject( obj, output );
    //            output.WriteEndDocument();
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public void Serialize( object obj, Stream output ) {
    //        try {
    //            using( XmlWriter writer = XmlWriter.Create( output ) ) {
    //                this.Serialize( obj, writer );
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public void Serialize( object obj, TextWriter output ) {
    //        try {
    //            using( XmlWriter writer = XmlWriter.Create( output ) ) {
    //                this.Serialize( obj, writer );
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public void Serialize( object obj, StringBuilder output ) {
    //        try {
    //            using( XmlWriter writer = XmlWriter.Create( output ) ) {
    //                this.Serialize( obj, writer );
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //}

    public class XmlObjectSerializer1 {

        public delegate void XmlObjectParser<T>( T t, ref string name, IDictionary<string, object> attributes, IList<object> childs );

        private Dictionary<Type, XmlObjectParser<object>> _typedParsers;
        private Dictionary<string, object> _attributesBuffer;

        public void AddParser<T>( XmlObjectParser<T> typedParser ) {
            try {
                if( typedParser.GetInvocationList().Length > 1 ) {
                    throw new Exception();
                }
                Type type = typeof( T );
                XmlObjectParser<object> parserAdapter = delegate( object obj, ref string name, IDictionary<string, object> attributes, IList<object> childs ) {
                    typedParser( ( T ) obj, ref name, attributes, childs );
                };
                if( this._typedParsers.ContainsKey( type ) ) {
                    this._typedParsers[ type ] += parserAdapter;
                } else {
                    this._typedParsers[ type ] = parserAdapter;
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XmlObjectSerializer1() {
            this._typedParsers = new Dictionary<Type, XmlObjectParser<object>>();
            this._attributesBuffer = new Dictionary<string, object>();
        }

        private void SerializeObject( object obj, XmlWriter writer ) {
            try {
                if( obj is IXPathNavigable ) {
                    writer.WriteNode( ( obj as IXPathNavigable ).CreateNavigator(), true );
                } else if( obj is XmlReader ) {
                    using( XmlReader reader = obj as XmlReader ) {
                        writer.WriteNode( reader, true );
                    }
                } else {
                    Type type = obj.GetType();
                    if( this._typedParsers.ContainsKey( type ) ) {
                        string name;
                        List<object> childs = new List<object>();
                        foreach( XmlObjectParser<object> parser in this._typedParsers[ type ].GetInvocationList() ) {
                            name = null;
                            this._attributesBuffer.Clear();
                            childs.Clear();
                            parser( obj, ref name, this._attributesBuffer, childs );
                            if( name != null && name.Length > 0 ) {
                                writer.WriteStartElement( name );
                                foreach( string attributeName in this._attributesBuffer.Keys ) {
                                    writer.WriteStartAttribute( attributeName );
                                    writer.WriteValue( this._attributesBuffer[ attributeName ] );
                                    writer.WriteEndAttribute();
                                }
                                foreach( object child in childs ) {
                                    this.SerializeObject( child, writer );
                                }
                                writer.WriteEndElement();
                                break;
                            } else if( childs.Count > 0 ) {
                                foreach( object child in childs ) {
                                    this.SerializeObject( child, writer );
                                }
                                break;
                            }
                        }
                    }
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public void Serialize( object obj, XmlWriter output ) {
            try {
                output.WriteStartDocument();
                this.SerializeObject( obj, output );
                output.WriteEndDocument();
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public void Serialize( object obj, Stream output ) {
            try {
                using( XmlWriter writer = XmlWriter.Create( output ) ) {
                    this.Serialize( obj, writer );
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public void Serialize( object obj, TextWriter output ) {
            try {
                using( XmlWriter writer = XmlWriter.Create( output ) ) {
                    this.Serialize( obj, writer );
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public void Serialize( object obj, StringBuilder output ) {
            try {
                using( XmlWriter writer = XmlWriter.Create( output ) ) {
                    this.Serialize( obj, writer );
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

    }

    //public class XmlObjectSerializer2 {

    //    public delegate bool Template<T>( T t, IContext context, object[] parameters );

    //    public interface IContext {

    //        void ApplyTemplates( object obj, params object[] parameters );
    //    }

    //    class Context : IContext {

    //        internal Dictionary<Type, Template<object>> _templates;
    //        internal XmlWriter _writer;

    //        public Context( Dictionary<Type, Template<object>> templates, XmlWriter writer ) {
    //            this._templates = templates;
    //            this._writer = writer;
    //        }

    //        public void ApplyTemplates( object obj, params object[] parameters ) {
    //            try {
    //                Type type = obj.GetType();
    //                if( this._templates.ContainsKey( type ) ) {
    //                    foreach( Template<object> template in this._templates[ type ].GetInvocationList() ) {
    //                        if( template( obj, this._writer, this, parameters ) ) {
    //                            return;
    //                        }
    //                    }
    //                }
    //            } catch( Exception ) {
    //                Debug.Assert( false );
    //                throw;
    //            }
    //        }
    //    }

    //    private Dictionary<Type, Template<object>> _templates;

    //    public XmlObjectSerializer2() {
    //        this._templates = new Dictionary<Type, Template<object>>();
    //    }

    //    public void AddTemplate<T>( Template<T> template ) {
    //        try {
    //            Type type = typeof( T );
    //            Template<object> templateAdapter = delegate( object obj, XmlWriter writer, IContext context, object[] parameters ) {
    //                return template( ( T ) obj, writer, context, parameters );
    //            };
    //            if( this._templates.ContainsKey( type ) ) {
    //                this._templates[ type ] += templateAdapter;
    //            } else {
    //                this._templates[ type ] = templateAdapter;
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public void Serialize( object obj, XmlWriter output, params object[] parameters ) {
    //        try {
    //            Context context = new Context( this._templates, output );
    //            output.WriteStartDocument();
    //            context.ApplyTemplates( obj, parameters );
    //            output.WriteEndDocument();
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public void Serialize( object obj, Stream output, params object[] parameters ) {
    //        try {
    //            using( XmlWriter writer = XmlWriter.Create( output ) ) {
    //                this.Serialize( obj, writer, parameters );
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public void Serialize( object obj, StringBuilder output, params object[] parameters ) {
    //        try {
    //            using( XmlWriter writer = XmlWriter.Create( output ) ) {
    //                this.Serialize( obj, writer, parameters );
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //}



    //public interface IXmlNodeBuilder<T> where T : IXmlNodeBuilder<T>, new() {

    //    string LocalName { set; }
    //    string Prefix { set; }
    //    string NamespaceUri { set; }
    //    void CreateAttribute( string localName, string prefix, string namespaceUri, object value );
    //    void CreateAttriubte( string localName, object value );
    //    void AppendChildText( object value );
    //    void AppendChildCData( object value );
    //    void AppendChildNode( object obj, params object[] templateParameters );
    //}

    //public class XmlObjectSerializer {

    //    public interface IXmlWriterBuffer {

    //        IEnumerable Childs { get; }
    //        void Write( XmlWriter writer );
    //    }

    //    public delegate bool Template<TObject, TBuffer>( TObject obj, TBuffer buf ) where TBuffer : IXmlWriterBuffer, new();
    //    delegate bool TemplateAdapter( object obj, out IXmlWriterBuffer buf );

    //    private Dictionary<Type, TemplateAdapter> _templates;

    //    public void AddTemplate<TObject, TBuffer>( Template<TObject, TBuffer> template ) where TBuffer : IXmlWriterBuffer, new() {
    //        try {
    //            Type objType = typeof( TObject );
    //            TemplateAdapter templateAdapter = delegate( object obj, out IXmlWriterBuffer buf ) {
    //                buf = new TBuffer();
    //                return template( ( TObject ) obj, ( TBuffer ) buf );
    //            };
    //            if( this._templates.ContainsKey( objType ) ) {
    //                this._templates[ objType ] += templateAdapter;
    //            } else {
    //                this._templates[ objType ] = templateAdapter;
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }

    //    public void Serialize( object obj, XmlWriter writer ) {
    //        try {
    //            Type type = obj.GetType();
    //            if( this._templates.ContainsKey( type ) ) {
    //                foreach( TemplateAdapter templateAdapter in this._templates[ type ].GetInvocationList() ) {
    //                    IXmlWriterBuffer buf;
    //                    if( templateAdapter( obj, out buf ) ) {
    //                        buf.Write( writer );
    //                        break;
    //                    }
    //                }
    //            }
    //        } catch( Exception ) {
    //            Debug.Assert( false );
    //            throw;
    //        }
    //    }
    //}

    //class XmlNodeBuilder : XmlObjectSerializer.IXmlNodeBuilder {

    //    #region IXmlNodeBuilder Members

    //    public string LocalName {
    //        set { throw new NotImplementedException(); }
    //    }

    //    public string Prefix {
    //        set { throw new NotImplementedException(); }
    //    }

    //    public string NamespaceUri {
    //        set { throw new NotImplementedException(); }
    //    }

    //    public void CreateAttribute( string localName, string prefix, string namespaceUri, object value ) {
    //        throw new NotImplementedException();
    //    }

    //    public void CreateAttriubte( string localName, object value ) {
    //        throw new NotImplementedException();
    //    }

    //    public void AppendChildText( object value ) {
    //        throw new NotImplementedException();
    //    }

    //    public void AppendChildCData( object value ) {
    //        throw new NotImplementedException();
    //    }

    //    public void AppendChildNode( object obj, params object[] templateParameters ) {
    //        throw new NotImplementedException();
    //    }

    //    #endregion

    //    #region IXmlWriterBuffer Members

    //    public void Clear() {
    //        throw new NotImplementedException();
    //    }

    //    public void Write( XmlWriter writer ) {
    //        throw new NotImplementedException();
    //    }

    //    #endregion
    //}
}
